Technical Note TN2017
Using Launch Services for discovering document binding and launching applications

目次

Launch Servicesは、Mac OS Xでアプリケーションを起動するための新しいAPIです。

Launch Servicesは、書類をアプリケーションにバインドし、システムレベルでアプリケーションを起動する機能を提供します。Desktop Databaseは、バンドルおよびファイルの拡張子を特定するには不十分でしたが、Launch Servicesは、アプリケーションを特定し起動するための適切な機能を提供します。

このテクニカルノートは、過去にDesktop Managerサービスを利用していた、あるいはアプリケーションをコードから起動する必要があるデベロッパを対象にしています。

[更新日:2001年4月17日]






Launch Services について

Launch Services については、Tech Pubs の文書の中で詳しく取り上げますが、できるだけ早く Launch Services を使い始めることが大事です。このテクニカルノートでは、Tech Pubs の文書の情報すべてはそろっていませんが、非常に重要な事項の実施方法を示します。ほかに LaunchServices.h の headerdoc 情報もとても役に立ちます。

Launch Services の主要目的は、アプリケーションの起動です。 Launch Services は、様々なフォーマットのアプリケーションを開始したり、特定のアプリケーションまたはデフォルトのアプリケーションを使って書類を開いたり、URL を開いたりするための、唯一の中間インタフェースとして機能します。Launch Services の役目は、起動リクエストを発行する前に、同じアプリケーションに関連づけられている書類すべてをひとまとまりのグループにし、起動リクエスト数を最小にすることです。Mac OS X の場合は、起動 API はいずれも、プリエンプティブ対応でも再入可能対応でもありません

Mac OS X とアプリケーションバンドルの登場で、Desktop Manager の有用さが失なわれました。アプリケーションバンドルは、データファイルおよび、plist に記述されているバンドル属性のみで構成されている場合があるので、Launch Services は、どの書類をどのアプリケーションで起動するかの情報を登録し維持するように作成されました。

Desktop Database API を使ってみると、その API はバンドルされたアプリケーションを無視することがわかります。Desktop Database 内でアプリケーションを探す方法の詳細については、"DTS Technote 1002, On Launching an App with a Document "を参照してください。

先頭に戻る

Launch Services を使用する利点

アプリケーションを見つけて起動するための API が Mac OS X で変更されましたが、これは良いことです。なぜなら現在の API の方が簡単だからです。以前は、Finder と同じように書類からアプリケーションを起動させるには、Desktop Manager、File Manager、Translation Manager、Internet Config、Process Manager および Apple Events などの数セットの API に関する知識が必要でした。現在これらのすべての機能は、使いやすい 1 つの API セットにまとめられました。

Launch Services は、アプリケーションまたは書類を開くよう Finder に対して要求しなければならなかったサードパーティ共通の必要性を排除するために作成されました。以前は Finder が、暗黙的に他の場所では利用できない形で、正しいアプリケーションを決定するための Translation Manager、Desktop Database、およびその他の情報を有していました。Launch Services は、アプリケーションを起動したり書類を開くためにクライアントが参照する唯一の場所としての機能を担うことでこの問題は解決します。Mac OS X の Finder は、Launch Services を呼び出す以外の処理は行いません。したがって、Launch Services を使用するクライアントはすべて、Finder と同じ動作をして、アプリケーションおよび書類を開きます。

先頭に戻る

書類とのバインド時の条件

書類を開くためのアプリケーションの特定は、主にアプリケーションの plist に設定されているいくつかの条件に基づいて行われます。ユーザは、特定のデータ型や特定の拡張子を持つファイルを開くためのアプリケーションを指定することにより、いくつかの設定情報をオーバーライドすることもできます。書類とアプリケーションの対応付けは一連のプライオリティに従って行われます。'????' のファイルのタイプとクリエータは、'0' と同じように処理されます。

  1. ユーザは、対象の書類に対して特定のアプリケーションを指定していますか?
  2. クリエータ情報が指定されている場合は、そのクリエーターを基にアプリケーションを探します。
  3. 拡張子情報が指定されている場合は、その拡張子を持つファイルを開けるアプリケーションを探します(拡張子の大文字小文字は区別されません)。
  4. ファイルのタイプが指定されている場合は、そのタイプのファイルを開けるアプリケーションを探します。
  5. いずれの場合でも、特定されたアプリケーションが、指定されている情報を持つ書類を処理できないことになっているアプリケーションであった場合には、このアプリケーションは不適切と判断され、スキップされます。
  6. ネイティブなアプリケーション、次に Classic アプリケーションという優先順位に従い、アプリケーション群の中から最初のアプリケーションを選択します。
  7. 同じ優先順位に同じアプリケーションがある場合は、最新バージョン番号を選択します。

これらの優先規則に従った例を次に示します。

ファイル名

タイプ

クリエータ

結果

Read Me.txt

TEXT

ttxt

クリエータが指定されているので Classic SimpleText

Read Me.txt

????

????

クリエータが指定されておらず、TextEdit は .txt のファイルを要求しており、アプリケーションがネーティブでありローカルにあるので TextEdit.app

Doc1.pdf

PDF_

CARO

クリエータが指定されており、ネーティブな Acrobat Reader がないので、Classic Acrobat Reader

Doc2.pdf

TEXT

????

クリエータが指定されておらず、TextEdit は .txt のファイルを要求しており、アプリケーションがネーティブでありローカルにあるので TextEdit.app

Index.html

TEXT

ttxt

クリエータが指定されているので、Classic SimpleText

Index.html

TEXT

????

クリエータが指定されておらず、IE-X は .html のファイルを要求しており、アプリケーションが、ネーティブでありローカルにあるので IE-X

Index.html

????

????

クリエータが指定されておらず、IE-X は .html のファイルを要求しており、アプリケーションが、ネーティブでありローカルにあるので IE-X

My Doc

TEXT

MSWD

クリエータが指定されているので、Classic Microsoft Word

My Doc

????

????

バインドなし。ユーザにアプリケーションを選択するように要求します

My Doc

TEXT

????

クリエータが指定されておらず、 TextEdit は 'OSType TEXT' のファイルであることを要求し、アプリケーションが、ネーティブでありローカルにあるのでTextEdit.app

Launch Services は、「最近使ったアプリケーション」と「最近使った書類」 の管理も行います。正しく起動されたアプリケーションおよび正しく開かれた書類のみが「最近使った」項目に追加されます。

先頭に戻る

デフォルトのアプリケーションの特定

現在 Launch Services は、Mac OS X の CFM および Mach-O のアプリケーションに対してのみ使用可能です。API は <LaunchServices.h> で利用できるので、Launch Services が使用できるかどうかは、リスト 1 に示されているように、実行時に T-Vector チェックを行って確認しなければなりません。Launch Services 機能が、後日 CarbonLib を使って Mac OS 8 または 9 にも導入されているかもしれないからです。<LaunchServices.h> を注意深く見ると、FSRefs および CFURL のどちらでも受け付ける API のバージョンが 2 つあることに気が付くでしょう。

Desktop Database の API を使ってみると、その API はバンドルされたアプリケーションを無視することがわかります。Desktop Database は、タイプとクリエータのペアに対して関連付けられている 1 つのバイナリアプリケーションを特定することがもともとの主な役割でした。Desktop Database の中でアプリケーションを特定する方法の詳細については、" DTS Technote 1002, On Launching an App with a Document" で参照してください。

次のコードは、ファイルの OSType に基づいてデフォルトのアプリケーションを見つける方法を示します。



if( (UInt32) LSGetApplicationForInfo !=
        (UInt32) kUnresolvedCFragSymbolAddress ) {
    err = LSGetApplicationForInfo(kLSUnknownType,
        'MSIE', nil, kLSRolesAll, &outAppRef, &outAppURL);
    if( err != noErr )
        return( err );
}

 リスト 1. このソースコードは、T-Vector をチェックすることにより、Launch Services が利用可能かどうかを検査し、指定されたアプリケーションのクリエータ、'MSIE' に基づいてデフォルトアプリケーションを探します。



同様に、OSType、ファイルの拡張子、役割などのいくつかの条件に基づて適切なアプリケーションを見つけることもできます。たとえば次のリスト 2 では、書類のファイル名の拡張子に基づいて、書類を表示する適切なアプリケーションを見つけるために LSGetApplicationForInfo() ルーチンを呼び出しています。



err = LSGetApplicationForInfo( kLSUnknownType,
                               kLSUnknownCreator,
                               CFSTR( "html" ),
                               kLSRolesViewer,
                               &outAppRef,
                               &outAppURL

 リスト 2. このソースコードは、.html ファイルを表示するよう指定されているアプリケーションを見つけます。



先頭に戻る

 

書類からのアプリケーションの起動

書類からアプリケーションを起動する場合、2 通りの方法があります。最も簡単で頻繁に使用される方法は、指定の書類を開くためにデフォルトのアプリケーションを起動することです(Finder でファイルをダブルクリックする動作と同じです)。 これは、LSOpenFSRef() を呼び出すことにより可能です。



EXTERN_API( OSStatus )

LSOpenFSRef( const FSRef *  inRef,              /* 開く項目 */
             FSRef *        outLaunchedRef );   /* NULLでもよい*/

 リスト 3. このソースコードは、開こうとしたファイルへの FSRef に基づいて、デフォルトのアプリケーションを使って対象ファイルを開く方法を示しています。





LSLaunchFSRefSpec inLaunchSpec;

    /* 使用するアプリケーション。Null でもよい*/

inLaunchSpec.appRef = &outAppRef;

    /* 開く/印刷する項目が Null でもよい*/

inLaunchSpec.numDocs = numDocuments;

    /* FSRefs の配列*/

inLaunchSpec.itemRefs = documentRefArray;

    /* 省略可能なパラメータとしてアプリケーションにそのまま渡される*/

inLaunchSpec.passThruParams = nil;

    /* デフォルト = 開く、非同期、Info.plist を使用、Classic として開始*/

inLaunchSpec.launchFlags = kLSLaunchDefaults;

    /* アプリケーションの起動/終了通知の受け取りを登録した場合に使用される*/

inLaunchSpec.asyncRefCon = nil;


err = LSOpenFromRefSpec( &inLaunchSpec, &outLaunchedRef );

 リスト 4. このソースコードは、より興味深い方法、つまり指定の書類を開くために指定のアプリケーションを起動する方法を示しています。



先頭に戻る

Navigation Services Filter Proc

アプリケーションが開けるファイルのみを表示したい場合は、Navigation Services filter proc を作成すれば良いでしょう。

Navigation Services filter proc の記述で大事なのは、各項目をアプリケーションで開ける対象にするかどうかを決定する部分です。この決定を行うためには、アプリケーションは、各項目について必要な情報を収集し、その項目をデベロッパのアプリケーションで開けるようにするかどうかを決めなければなりません。たとえば、テキスト編集用アプリケーションの plist が、'TEXT' タイプのファイル、およびファイル名に txt または text の拡張子を持つファイルを開けると記述している場合、この汎用の filter proc によって、Launch Services にクエリが発行され、アプリケーションが開けるファイルのみが自動的に表示されます。



pascal Boolean NavLaunchServicesFilterProc( AEDesc* theItem,
                                            void* info,
                                            NavCallBackUserData ioUserData,
                                            NavFilterModes filterMode)
{
    #pragma unused( info, ioUserData )

    LSItemInfoRecord infoRec;
    FSRef            fsRef;
    OSStatus         err      = noErr;
    Boolean          showItem = false;

    if( filterMode == kNavFilteringBrowserList ) {
        err = GetFSRefFromAEDesc( &fsRef, theItem );

        if ( err != noErr ) goto BailWithError;

           /* LaunchServices に対してパッケージ化されて
            いるかどうか、またファイルのタイプとクリエータを照会する */

        err = LSCopyItemInfoForRef( &fsRef, kLSRequestAllInfo, &infoRec );

        if( ( err != noErr ) && ( err != kLSApplicationNotFoundErr ) )

            goto BailWithError;

            /* この項目を開けることができるようにするかどうかを判定する。
       この項目がフォルダであり(パッケージでない)場合は必ず、
             ユーザがその内容を確認できるようにする

            Boolean isPkg =
                ( ( infoRec.flags & kLSItemInfoIsPackage ) != 0 );

            Boolean isApp =
                ( ( infoRec.flags & kLSItemInfoIsApplication ) != 0 );

            */

        if ( ( infoRec.flags & kLSItemInfoIsAliasFile ) != 0 ) {

            /* エイリアスを解決し、オリジナルの情報に基づいて、
       その項目を選択するかどうかを決めることもできる。
       どうするかは、読者の練習のために残しておく。 */

        }

        if ( ( infoRec.flags & kLSItemInfoIsContainer ) != 0 ) {
            showItem = true;
        }
        else {
            Boolean canViewItem = false;

            err = LSCanRefAcceptItem( &fsRef, &gApplicationFSRef,
                kLSRolesViewer, kLSAcceptDefault, &canViewItem );
            if( err == noErr )
                showItem = canViewItem;
        }
    }
    else {
        showItem = true;
    }
    return( showItem );

BailWithError:
    return( false );
}

OSStatus GetFSRefFromAEDesc( FSRef *fsRef, AEDesc* theItem )
{
    OSStatus err        = noErr;
    AEDesc   coerceDesc = { NULL, NULL };

    /* AEDesc がまだ FSSpec でない場合は、
       FSSpec に変換する... */

    if( theItem->descriptorType != typeFSRef ) {
        err = AECoerceDesc( theItem, typeFSRef, &coerceDesc );
        if( err == noErr )
            theItem = &coerceDesc;
    }

    /* AEDesc から FSRef を取得する*/

    if( err == noErr )
        err = AEGetDescData( theItem, fsRef, sizeof(*fsRef) );

    AEDisposeDesc( &coerceDesc );

    /* FSRef を取得できなかった場合は、FSSpec の取得を行い、
         そこから FSRef を作成する。 */

    if( err != noErr ) {
        FSSpec fsSpec;

        AEDesc coerceDesc2 = {NULL, NULL};

        /* AEDesc がまだ FSSpec でない場合は、FSSpec に変換する... */

        if ( theItem->descriptorType != typeFSS ) {
            err = AECoerceDesc( theItem, typeFSS, &coerceDesc2 );
            theItem = &coerceDesc2;
        }

        /* AEDesc から FSSpec を取得し、FSRef に変換する */

        if ( err == noErr )
           err = AEGetDescData( theItem, &fsSpec, sizeof(fsSpec) );

        AEDisposeDesc( &coerceDesc2 );

        if ( err == noErr )
            err = FSpMakeFSRef( &fsSpec, fsRef );
    }
    return( err );
}

リスト 5. アプリケーションにより開けると Launch Services が判断した項目のみを表示する NavigationServices filter proc のソースコード。これは、たとえばテキスト編集用のアプリケーションが、拡張子はついているがタイプがわからないファイルを開かなければならない場合にとりわけ重要です。サンプルコードはここから ダウンロード できます。



先頭に戻る

その他の共通ルーチン

リスト 5 で使用される LSCopyItemInfoForRefおよびそれに関連する関数が、パッケージ情報の収集に使われます。それが、Carbon アプリケーションまたは Classic アプリケーションかどうか、パッケージ化されているかどうか、アプリケーションファイルのタイプとクリエータ、およびその他の情報について収集します。ヘッダファイルの LaunchServices.h でも記述されているように、このルーチンのいくつかのバリケーションのいくつかはスレッドセーフです。

デベロッパは、ファイル、フォルダ、エイリアス、CodeWarrior テキストファイルなど、Finder のリストビュー内に表示されているものと同じ文字列によるファイルの「種類」を表示させることができます。LSCopyKindStringForRef および LSCopyKindStringForURL は、ファイルの種類の情報を含む CFStringRef を返します。

先頭に戻る

参考文献



重要:
アプリケーションの plist ファイルを正しく設定するためには、必ず DTS Technote 2013, The 'plst' Resource を読んでください。



Desktop Databaseに関しては、テクニカルノート1002: On Launching an App with a Documentも読んでください。

先頭に戻る

ダウンロード

Redbook gif

リスト5のNavigation Servicesフィルタコード(28 KB)

ダウンロード


先頭に戻る